package ge.unihack.instantdj.protocol.server;

import ge.unihack.instantdj.protocol.common.Command;
import ge.unihack.instantdj.protocol.common.CommandWriter;
import ge.unihack.instantdj.protocol.common.JSONCommandWriter;
import ge.unihack.instantdj.utils.NetworkUtils;

import java.io.IOException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;

public class InstantDJServer implements Server, ClientListener {

	private List<ClientWrapper> clients = new ArrayList<ClientWrapper>();
	private CommandWriter commandWriter = new JSONCommandWriter();
	private ClientListenerThread listenerThread;
	private volatile boolean accepting;
	private int port;

	@Override
	public void start() throws IOException {
		if (listenerThread == null) {
			listenerThread = new ClientListenerThread(this);
			port = listenerThread.startListening();
			accepting = true;
		}
	}

	public void setCommandWriter(CommandWriter commandWriter) {
		this.commandWriter = commandWriter;
	}

	@Override
	public void sendCommand(Command command, Client client) throws IOException {
		ClientWrapper cw;
		if (client instanceof ClientWrapper) {
			cw = (ClientWrapper) client;
		} else {
			cw = findClientWrapper(client.getId());
		}
		if (cw != null) {
			commandWriter.writeCommand(cw.getOut(), command);
		}
	}

	@Override
	public void onClientConnected(ClientWrapper client) {
		if (accepting) {
			synchronized (clients) {
				clients.add(client);
			}
		}
	}

	@Override
	public void onClientDisconnected(ClientWrapper client) {
		if (accepting) {
			synchronized (clients) {
				clients.remove(client);
			}
		}
	}

	@Override
	public List<Client> getConnectedClients() {
		List<Client> cclients = new ArrayList<Client>(clients.size());
		synchronized (clients) {
			Collections.copy(cclients, clients);
		}
		return cclients;
	}

	@Override
	public String getLocalAddress() {
		return NetworkUtils.getIPAddress();
	}

	@Override
	public int getLocalPort() {
		return port;
	}

	private ClientWrapper findClientWrapper(int id) {
		List<Client> connectedClients = getConnectedClients();
		for (Client cw : connectedClients) {
			if (cw.getId() == id) {
				return (ClientWrapper) cw;
			}
		}
		return null;
	}

	@Override
	public void stop() {
		if (listenerThread != null) {
			accepting = false;
			for (ClientWrapper cl : clients) {
				cl.close();
			}
			listenerThread.stopListening();
			listenerThread = null;
		}
	}

}
